// 草稿
//
// 注意:
// 不要在函数里做如下操作，因为 items 是 initialize_list<T>，它本质是个指针，
// 下面初始化的 menu_item 在出了函数作用域后就析构了，那么 items 将指向析构的数组
// inline menu create_xxx(...){
//     return menu{
//         ...
//         .items = {
//             menu_item{
//                 ...
//             },
//             menu_item{
//                 ...
//             },
//         }
//     };
// }
#ifndef xpack_ttyctrl_menu
#define xpack_ttyctrl_menu
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::ttyctrl_menu::inc
#include"algo/heap_root.hpp"
#include"define/base_type.hpp"
#include"docker/page_array.hpp"
#include"docker/stack.hpp"
#include"interface/can_callback.hpp"
#include"interface/initializer_list.hpp"
#include"lang/cxx.hpp"
#include"macro/xcstyle.hpp"
#include"macro/xexport.hpp"
#include"macro/xmaybe_unused.hpp"
#include"ttyctrl/text_list.hpp"
#pragma pop_macro("xuser")

namespace mixc::ttyctrl_menu::origin{
    struct menu;
    struct menu_stack;

    typedef struct menu_item{
        enum hslide_t : uxx{
            turn_left,
            turn_right,
        };
        using click_action_t                = void(*)(menu_stack & stack, menu_item * self, uxx i_item);
        using stop_action_t                 = click_action_t;
        using slide_action_t                = void(*)(menu_stack & stack, menu_item * self, uxx i_item, hslide_t mode);
        inc::c08        content;
        click_action_t  on_click;                               // 点击却热恩
        slide_action_t  on_hslide           = nullptr;          // 水平滑动，类似滑动条，通过左右按键调大调小数值
    } * menu_itemp;

    struct menu_item_list{
        using get_item_t                    = menu_item(*)(uxx i);
        using get_length_t                  = uxx(*)();
        get_length_t    get_length          = nullptr;
        get_item_t      get_item            = nullptr;
    };

    struct menu{
        enum page_policy_t : uxx{
            allowed,        // 允许
            forbidden,      // 阻止操作
        };

        using action_refesh_t               = uxx(*)(menu_stack & stack, menu * self);
        using action_before_back_t          = page_policy_t(*)(menu_stack & stack, menu * self);
        inc::c08                            title;

        // 约定:
        // items 和 items_dyna 只能二选一使用
        inc::initializer_list<menu_item>    items;
        menu_item_list                      items_dyna = {}; // 动态元素

        // 返回值：value
        // value != not_exist，表示每格 value 毫秒就重新执行 refresh
        // value == not_exist or refresh == nullptr，表示静态页面
        action_refesh_t                     refresh = nullptr;

        action_before_back_t                on_return_key_press = nullptr;

        // 如果嵌套多层的子页面点击退出，需要退到 is_exit_point = true 的页面
        bool                                is_exit_point = false;
    };

    struct menu_stack{
    private:
        // i 表示当前窗口第几个元素，比如窗口只能看到 8 个元素，那么 i 的范围属于 0~7，
        // i 和 i_item 的区别在于，i_item 是元素在列表中的绝对位置，列表可能有很多个元素，而 i 是相对于窗口而言的位置
        using foreach_invoke        = inc::icallback<loop_t(uxx i, menu_item & item, bool is_selected), loop_t::go_on> const &;
        inc::stack<menu *>          page;
        inc::stack<inc::text_list>  page_cursor;
        inc::stack<bool>            page_need_refresh;
        uxx                         default_rows;

        // 每次进入新的页面时，需要加载它对应的 menu::refresh 函数
        menu::action_refesh_t       refresh_page = nullptr;
    public:
        menu_stack(uxx default_rows) : page{}, page_cursor{}, default_rows(default_rows), refresh_page{}{}

        uxx i_item_of_depth(uxx i_depth){
            auto i_item             = uxx{};
            page_cursor.foreach([&](uxx i, inc::text_list const & page){
                i_item              = page->i_current;
                return i == i_depth;
            });
            return i_item;
        }

        void page_push(menu * list){
            auto tui                = inc::text_list{};
            tui->count              = this->item_counts(*list);
            tui->count_of_visiable  = default_rows;
            refresh_page            = list->refresh;
            page.push(list);
            page_cursor.push(tui);
            page_need_refresh.push(refresh_page != nullptr);
        }

        menu * page_pop(){
            auto top                = page.pop()->value();
            page_need_refresh.pop();
            page_cursor.pop(); // cursor 跟着 pop

            // 需要复位 refresh_page，避免残留到回退的页面
            if (refresh_page = nullptr; page_need_refresh->is_empty == false){
                if (page_need_refresh->top){
                    refresh_page    = this->top_page()->refresh;
                }

                // 在每次 return 到上一个页面前检查元素个数是否变化
                auto new_count      = this->item_counts(*page->top);
                auto & cursor       = page_cursor->top;
                cursor->count       = new_count;
            }
            return top;
        }

        menu * top_page(){
            return page->is_empty ? nullptr : page->top();
        }

        void foreach_current_window(foreach_invoke iterator){
            auto cursor             = page_cursor->top();
            this->foreach_menu_item(cursor->i_top, cursor->i_current, cursor->i_bottom, iterator);
        }

        void up(){ // 光标上移
            this->move([](auto & tui){ tui->up(); });
        }

        void down(){ // 光标下移
            this->move([](auto & tui){ tui->down(); });
        }

        void left(){
            this->hslide(menu_item::turn_left);
        }

        void right(){
            this->hslide(menu_item::turn_right);
        }

        void click(){ // 点击
            auto cursor             = page_cursor->top();
            auto i_curr             = cursor->i_current();
            this->foreach_menu_item(i_curr, i_curr, i_curr,
                [&](xmaybe_unused uxx i_item, xmaybe_unused menu_item & item, xmaybe_unused bool is_selected){
                    if (item.on_click){
                        item.on_click(*this, &item, i_curr);
                    }
                }
            );
        }

        void back(){ // 返回
            if (auto page = this->top_page(); page == nullptr){
                return;
            }
            else if(
                page->on_return_key_press == nullptr or
                page->on_return_key_press(*this, page) == page->allowed){
                this->page_pop();
            }
        }

        uxx refresh(){ // 执行刷新函数，返回下一次刷新的毫秒延时，如果返回 not_exist 则表示不再需要刷新
            auto time_span_ms       = not_exist;
            auto top                = this->top_page();

            if (refresh_page){
                if (time_span_ms = refresh_page(*this, top); time_span_ms == not_exist){
                    if (refresh_page = nullptr;/*无需再刷新*/ time_span_ms == not_exist){
                        page_need_refresh->top = false;
                    }
                }
            }

            return time_span_ms;
        }

        void go_exit_point(){
            while(page->is_empty == false){
                if (this->page_pop(); this->top_page()->is_exit_point){ // 至少 pop 一个页面，因为当前页面自身可能也是 exit point
                    return;
                }
            }
        }

    private:
        void move(auto && action){
            auto & curr             = page_cursor->top;
            auto c                  = curr->count();

            for(auto page = this->top_page(); c--;){
                auto & curr         = page_cursor->top;
                auto i_item         = curr->i_current();
                if (action(curr); this->is_selectable(page, i_item)){ // 跳过不能选中
                    break;
                }
            }
        }

        void hslide(menu_item::hslide_t mode){
            auto cursor             = page_cursor->top();
            auto i_curr             = cursor->i_current();
            this->foreach_menu_item(i_curr, i_curr, i_curr,
                [&](xmaybe_unused uxx i_item, xmaybe_unused menu_item & item, xmaybe_unused bool is_selected){
                    if (item.on_hslide){
                        item.on_hslide(*this, &item, i_curr, mode);
                    }
                }
            );
        }

        bool is_selectable(xmaybe_unused menu * page, xmaybe_unused uxx i_currrent){
            // 约定:
            // items 和 items_dyna 只能二选一使用
            // if (page->items.size()){
            //     // 约定:
            //     // 如果不能点击，就无法选中
            //     return page->items.begin()[i_currrent].on_click != nullptr;
            // }
            // else if (page->items_dyna->length()){
            //     return page->items_dyna[i_currrent].on_click != nullptr;
            // }
            // else{
            //     return true;
            // }

            // 有可能全是不可点击的
            return true;
        }

        uxx item_counts(menu const & list){
            // 约定:
            // items 和 items_dyna 只能二选一使用
            uxx length      = list.items.size() ? list.items.size() : list.items_dyna.get_length ? list.items_dyna.get_length() : 0;
            return length;
        }

        void foreach_menu_item(uxx i_begin, uxx i_curr, uxx i_end, foreach_invoke invoke){
            // 约定:
            // items 和 items_dyna 只能二选一使用
            if (auto page = this->top_page(); page->items.size()){
                for(uxx i_item = i_begin; i_item <= i_end; i_item++){
                    if (auto & curr = *menu_itemp(page->items.begin() + i_item);
                        // invoke 第一个参数需要
                        invoke(i_item - i_begin, curr, i_item == i_curr) == loop_t::finish){
                        return;
                    }
                }
            }
            else if (auto length = page->items_dyna.get_length ? page->items_dyna.get_length() : uxx{}; length){
                for(uxx i_item = i_begin; i_item <= i_end; i_item++){
                    auto item       = page->items_dyna.get_item(i_item);
                    auto i          = i_item - i_begin; // i 是从 0 开始计数
                    if (invoke(i, item, i_item == i_curr) == loop_t::finish){
                        return;
                    }
                }
            }
        }
    };

    constexpr auto no_return = xcstyle(xmaybe_unused menu_stack & stack, xmaybe_unused menu * self){
        return menu::forbidden; // 此页面按取消按钮不会返回上一个页面
    };

    constexpr auto page_pop = xcstyle(xmaybe_unused menu_stack & stack, xmaybe_unused menu_item * self, xmaybe_unused uxx i_item){
        stack.page_pop();
    };

    constexpr auto go_exit_point = xcstyle(xmaybe_unused menu_stack & stack, xmaybe_unused menu_item * self, xmaybe_unused uxx i_item){
        stack.go_exit_point(); // 退到第一个 is_exit_point = true 的页面
    };
}

#endif

xexport_space(mixc::ttyctrl_menu::origin)
